/**
 * Copyright 2010 Christophe Vandeplas
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. 
 */

package de.schaeuffelhut.android.openvpn.util;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import android.util.Log;

/**
 * @author chri
 *
 */
public class TrafficStats {

	private final static String LOG_TAG = "TrafficStats";
	public static final int mPollInterval = 3;
	
	private Date updated = new Date();
	DateFormat dateFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy");
	private int tuntapReadBytes = 0;
	private int tuntapWriteBytes = 0;
	private int tcpudpReadBytes = 0;
	private int tcpudpWriteBytes = 0;
	private int authReadBytes = 0;
	private int preCompressBytes = 0;
	private int postCompressBytes = 0;
	private int preDecompressBytes = 0;
	private int postDecompressBytes = 0;
	
	private int tuntapReadBytesPerSec = 0;
	private int tuntapWriteBytesPerSec = 0;
	private int tcpudpReadBytesPerSec = 0;
	private int tcpudpWriteBytesPerSec = 0;
	private int authReadBytesPerSec = 0;
	private int preCompressBytesPerSec = 0;
	private int postCompressBytesPerSec = 0;
	private int preDecompressBytesPerSec = 0;
	private int postDecompressBytesPerSec = 0;
	
	
	public TrafficStats() {
		
	}
	
	public void setStats(final int newReadBytes, final int newWriteBytes) {
		tuntapReadBytesPerSec = deltaPerSecond(tuntapReadBytes, newReadBytes);
		tuntapReadBytes = newReadBytes;
		
		tuntapWriteBytesPerSec = deltaPerSecond(tuntapWriteBytes, newWriteBytes);
		tuntapWriteBytes = newWriteBytes;
	}

	private int deltaPerSecond(int oldBytes, int newBytes) {
		return ((newBytes - oldBytes)/ mPollInterval);
	}
	
	/**
	 * Fill in the statistics based on the OpenVPN MultiLine status output
	 * @param multiLineHistory and ArrayList containing the output from openvpn
	 */
	@Deprecated
	public void setStats(ArrayList<String> multiLineHistory) {
//		OpenVPN STATISTICS
//		Updated,Sun Nov 15 15:49:24 2009
//		TUN/TAP read bytes,5261
//		TUN/TAP write bytes,0
//		TCP/UDP read bytes,7115
//		TCP/UDP write bytes,10493
//		Auth read bytes,64
//		pre-compress bytes,0
//		post-compress bytes,0
//		pre-decompress bytes,0
//		post-decompress bytes,0
		
		Iterator<String> iterator = multiLineHistory.iterator();
		// skip the first command as it will always be the same
		iterator.next();
		
		// now loop over every line and fill in the variables
		while (iterator.hasNext()) {
			String line = iterator.next();
			String command = null, value = null;
			// search for the comma "," and split the message in two 
			Matcher matcher = Pattern.compile("^(.*),(.*)$").matcher(line);
			if (matcher.find()) {
				command = matcher.group(1);
				value = matcher.group(2);
			}
			else {
				Log.e(LOG_TAG, "ERROR: following status line could not be split: " + line);
				break;
			}

			if (0 == command.compareTo("Updated")) {
				// date of last update
				try {
					updated = dateFormat.parse(value);
				} catch (ParseException e) {
					Log.e(LOG_TAG, "Cannot parse date: " + value);
				}
			} else if (0 == command.compareTo("TUN/TAP read bytes")) {
				// traffic from the tunnel
				int newTuntapReadBytes = Integer.parseInt(value);
				tuntapReadBytesPerSec = ((newTuntapReadBytes - tuntapReadBytes)/ getDivideFactor());
				tuntapReadBytes = newTuntapReadBytes;
			} else if (0 == command.compareTo("TUN/TAP write bytes")) {
				// traffic to the tunnel
				int newTuntapWriteBytes = Integer.parseInt(value);
				tuntapWriteBytesPerSec = ((newTuntapWriteBytes - tuntapWriteBytes)/ getDivideFactor());
				tuntapWriteBytes = newTuntapWriteBytes;
			} else if (0 == command.compareTo("TCP/UDP read bytes")) {
				// traffic generated by the tunnel
				int newTcpudpReadBytes = Integer.parseInt(value);
				tcpudpReadBytesPerSec = ((newTcpudpReadBytes - tcpudpReadBytes)/ getDivideFactor());
				tcpudpReadBytes = newTcpudpReadBytes;
			} else if (0 == command.compareTo("TCP/UDP write bytes")) {
				// traffic generated by the tunnel
				int newTcpudpWriteBytes = Integer.parseInt(value);
				tcpudpWriteBytesPerSec = ((newTcpudpWriteBytes - tcpudpWriteBytes)/ getDivideFactor());
				tcpudpWriteBytes = newTcpudpWriteBytes;
			} else if (0 == command.compareTo("Auth read bytes")) {
				// traffic because of the authentication
				int newAuthReadBytes = Integer.parseInt(value);
				authReadBytesPerSec = ((newAuthReadBytes - authReadBytes)/ getDivideFactor());
				authReadBytes = newAuthReadBytes;
			} else if (0 == command.compareTo("pre-compress bytes")) {
				// size before compression
				int newPreCompressBytes = Integer.parseInt(value);
				preCompressBytesPerSec = ((newPreCompressBytes - preCompressBytes)/ getDivideFactor());
				preCompressBytes = newPreCompressBytes;
			} else if (0 == command.compareTo("post-compress bytes")) {
				// size after compression
				int newPostCompressBytes = Integer.parseInt(value);
				postCompressBytesPerSec = ((newPostCompressBytes - postCompressBytes)/ getDivideFactor());
				postCompressBytes = newPostCompressBytes;
			} else if (0 == command.compareTo("pre-decompress bytes")) {
				// size before decompression
				int newPreDecompressBytes = Integer.parseInt(value);
				preDecompressBytesPerSec = ((newPreDecompressBytes - preDecompressBytes)/ getDivideFactor());
				preDecompressBytes = newPreDecompressBytes;
			} else if (0 == command.compareTo("post-decompress bytes")) {
				// size after decompression
				int newPostDecompressBytes = Integer.parseInt(value);
				postDecompressBytesPerSec = ((newPostDecompressBytes - postDecompressBytes)/ getDivideFactor());
				postDecompressBytes = newPostDecompressBytes;
			} else {
				// should never happen
				Log.e(LOG_TAG, "ERROR: following status line was not understood: "
						+ line);
			}
		}

	}
	
	@Deprecated
	private int getDivideFactor() {
		return mPollInterval ;
	}
	
	public String toSmallInOutPerSecString() {
		// TODO chri - use stringbuilder
		return "up: " 
				+ Util.roundDecimalsToString((double) tuntapReadBytesPerSec / 1000) 
				+ " kBps - down: "
				+ Util.roundDecimalsToString((double) tuntapWriteBytesPerSec / 1000) 
				+ " kBps";
	}
	
	
}
